#include "OS_gui.h"

static WINDOW *menuWin, *regWin, *textWin, *asmWin, *wpWin;
static int winRow, winCol;
static int cmd_y, cmd_x, win_y, win_x;
static char **textLine, **asmLine, **history;
static int *textColor;
static uint32_t reg_cache[64] = {0};  // 32:pc  33:hi  34:lo
static int textPoint, asmPoint, history_cnt;
static MEVENT mouse;
static struct{
	const char* cmd_Name;
	int cmd_id;
}menu_list[] = {
	{ "Continue", _continue },
	{ "Single", _single_step },
    { "ReBuild", _rebuild },
    { "ReStart", _restart },
    { "Quit", _quit },
};


void init_Screen(){
    GUI_fp = fopen("GUI_debug.log", "w");
    log("init_Screen");
    initscr();
    noecho();
    curs_set(0);
    keypad(stdscr, true);
    cbreak();
    mousemask(ALL_MOUSE_EVENTS, NULL);
    getmaxyx(stdscr, winRow, winCol);

    if(menuWin == NULL){
        menuWin = subwin(stdscr, MENU_ROW, MENU_COL, 0, 0);
        log("menu窗口初始化完成");
    }
    if(regWin == NULL){
        regWin = subwin(stdscr, REG_ROW, REG_COL, 0, MENU_COL);
        log("reg窗口初始化完成");
    }
    if(textWin == NULL){
        textWin = subwin(stdscr, TEXT_ROW, TEXT_COL, MENU_ROW, 0);
        log("text窗口初始化完成");
    }
    if(asmWin == NULL){
        asmWin = subwin(stdscr, ASM_ROW, ASM_COL, MENU_ROW, TEXT_COL);
        log("asm窗口初始化完成");
    }
    if(wpWin == NULL){
        wpWin = subwin(stdscr, WP_ROW, WP_COL, REG_ROW, MENU_COL);
        log("wp窗口初始化完成");
    }

    init_Color();
    init_Text();
    init_Asm();
    // init_History();
}

void init_Text(){
    log("init_Text");
    int i;
    int text_Row = TEXT_ROW - BOX_SIDE;
    int text_Col = TEXT_COL - BOX_SIDE;
    textPoint = 0;
    log("当前Text窗口参数为[%d * %d]", text_Row, text_Col);
    textLine = (char**)malloc(text_Row * sizeof(char*));
    textColor = (int*)malloc(text_Row * sizeof(int));
    for(i = 0; i < text_Row; ++i){
        textLine[i] = (char*)malloc(text_Col * sizeof(char));
        strcpy(textLine[i], "\n");
        textColor[i] = WHITE_TEXT;
    }
    log("Text表初始化完成");
}

void init_Asm(){
    log("init_Asm");
    int i;
    int asm_Row = ASM_ROW - BOX_SIDE;
    int asm_Col = ASM_COL - BOX_SIDE;
    asmPoint = 0;
    log("当前Asm窗口参数为[%d * %d]", asm_Row, asm_Col);
    asmLine = (char**)malloc(asm_Row * sizeof(char*));
    for(i = 0; i < asm_Row; ++i){
        asmLine[i] = (char*)malloc(asm_Col * sizeof(char));
        strcpy(asmLine[i], "\n");
    }
    log("Asm表初始化完成");
}

void init_Color(){
    if(has_colors() == false){
        add_Text("Your terminal does not support color", RED_TEXT);
    }
    else start_color();
    if(can_change_color() == false){
        add_Text("Your terminal does not support to change color", RED_TEXT);
    }

    init_pair(WHITE_TEXT, COLOR_WHITE, COLOR_BLACK);
    init_pair(RED_TEXT, COLOR_RED, COLOR_BLACK);
    init_pair(BLUE_TEXT, COLOR_BLUE, COLOR_BLACK);
    init_pair(YELLOW_TEXT, COLOR_YELLOW, COLOR_BLACK);
    init_pair(GREEN_TEXT, COLOR_GREEN, COLOR_BLACK);
}

void init_History(){
    history = (char**)malloc(1024*sizeof(char*));
    history_cnt = 0;
}

void test_Screen(){
    log("test_Screen");
    mvwprintw(menuWin, 1, 1, "This is Menu.");
    box(menuWin, BOX_SPE, BOX_SPE);
    mvwprintw(regWin, 1, 1, "This is Reg.");
    box(regWin, BOX_SPE, BOX_SPE);
    mvwprintw(textWin, 1, 1, "This is Text.");
    box(textWin, BOX_SPE, BOX_SPE);
    mvwprintw(asmWin, 1, 1, "This is Asm.");
    box(asmWin, BOX_SPE, BOX_SPE);
    mvwprintw(wpWin, 1, 1, "This is Wp.");
    box(wpWin, BOX_SPE, BOX_SPE);
    log("test Done");
}

void del_Screen(){
    log("del_Screen");
    delwin(menuWin);
    delwin(regWin);
    delwin(textWin);
    delwin(asmWin);
    delwin(wpWin);
    keypad(stdscr, false);
    curs_set(1);
    echo();
    endwin();
    menuWin = NULL;
    regWin = NULL;
    textWin = NULL;
    asmWin = NULL;
    wpWin = NULL;
    // free(textLine);
    // free(asmLine);
    // free(textColor);
    // fclose(GUI_fp);
    log("del Done");
}

void draw_Menu(){
    log("draw_Menu");
    wclear(menuWin);
    int y = 1, x = 1;
    int i;
    getparyx(menuWin, win_y, win_x);
    for(i = 0; i < NR_MENU; ++i){
        wmove(menuWin, y, x);
        getyx(menuWin, cmd_y, cmd_x);
        set_cmdL(menu_list[i].cmd_id, win_y + cmd_y, win_x + cmd_x);
        wprintw(menuWin, menu_list[i].cmd_Name);
        x += CMD_STEP;
    }
    box(menuWin, BOX_SPE, BOX_SPE);
    wrefresh(menuWin);
}

void draw_Reg(){
    log("draw_Reg");
    wclear(regWin);
    int y = 1, x = 1;
    int i;
    mvwprintw(regWin, y++, x, "Reg box:");
    if(reg_cache[32] != cpu.pc) {
    wattron(regWin, COLOR_PAIR(YELLOW_TEXT));
    mvwprintw(regWin, y++, x, "$pc:\t0x%08x", cpu.pc);
    reg_cache[32] = cpu.pc;
    wattroff(regWin, COLOR_PAIR(YELLOW_TEXT));
    }
    else mvwprintw(regWin, y++, x, "$pc:\t0x%08x", cpu.pc);
    for(i = 0; i < NR_REG; i += 2){
        if(reg_cache[i] != cpu.gpr[i]._32) {
        wattron(regWin, COLOR_PAIR(YELLOW_TEXT));
        mvwprintw(regWin, y++, x, "%s:\t0x%08x\t",regfile[i], cpu.gpr[i]._32);
        reg_cache[i] = cpu.gpr[i]._32;
        wattroff(regWin, COLOR_PAIR(YELLOW_TEXT));
        }
        else mvwprintw(regWin, y++, x, "%s:\t0x%08x\t",regfile[i], cpu.gpr[i]._32);

        if(reg_cache[i+1] != cpu.gpr[i+1]._32) {
        wattron(regWin, COLOR_PAIR(YELLOW_TEXT));
        wprintw(regWin, "%s:\t0x%08x", regfile[i+1], cpu.gpr[i+1]._32);
        reg_cache[i+1] = cpu.gpr[i+1]._32;
        wattroff(regWin, COLOR_PAIR(YELLOW_TEXT));
        }
        else wprintw(regWin, "%s:\t0x%08x", regfile[i+1], cpu.gpr[i+1]._32);
    }
    if(reg_cache[33] != cpu.hi) {
    wattron(regWin, COLOR_PAIR(YELLOW_TEXT));
    mvwprintw(regWin, y++, x, "$hi:\t0x%08x\t",cpu.hi);
    reg_cache[33] = cpu.hi;
    wattroff(regWin, COLOR_PAIR(YELLOW_TEXT));
    }
    else mvwprintw(regWin, y++, x, "$hi:\t0x%08x\t",cpu.hi);

    if(reg_cache[34] != cpu.lo) {
    wattron(regWin, COLOR_PAIR(YELLOW_TEXT));
    wprintw(regWin, "$lo:\t0x%08x", cpu.lo);
    reg_cache[34] = cpu.lo;
    wattroff(regWin, COLOR_PAIR(YELLOW_TEXT));
    }
    else wprintw(regWin, "$lo:\t0x%08x", cpu.lo);

    box(regWin, BOX_SPE, BOX_SPE);
    wrefresh(regWin);
}

void draw_Text(){
    log("draw_Text");
    wclear(textWin);
    int y = 1, x = 1;
    int i;
    int text_Row = TEXT_ROW - BOX_SIDE;
    for(i = 0; i < text_Row; ++i){
        wattron(textWin, COLOR_PAIR(textColor[(textPoint + i)%text_Row]));
        log("Color: %d", textColor[(textPoint + i)%text_Row]);
        mvwprintw(textWin, y++, x, textLine[(textPoint + i)%text_Row]);
        wattroff(textWin, COLOR_PAIR(textColor[(textPoint + i)%text_Row]));
    }
    box(textWin, BOX_SPE, BOX_SPE);
    wrefresh(textWin);
}

void draw_Asm(){
    log("draw_Asm");
    wclear(asmWin);
    int y = 1, x = 1;
    int i;
    int asm_Row = ASM_ROW - BOX_SIDE;
    for(i = 0; i < asm_Row; ++i){
        mvwprintw(asmWin, y++, x, asmLine[(asmPoint + i)%asm_Row]);
    }

    box(asmWin, BOX_SPE, BOX_SPE);
    wrefresh(asmWin);
}

void draw_Wp(){
    log("draw_Wp");
    wclear(wpWin);
    int y = 1, x = 1;
    int i;
    getparyx(wpWin, win_y, win_x);
    mvwprintw(wpWin, y++, x, "WatchPoint: %d", getUsedWpNum());
    wmove(wpWin, y, x);
    getyx(wpWin, cmd_y, cmd_x);
    set_cmdL(_add_wp, win_y + cmd_y, win_x + cmd_x);
    wprintw(wpWin,"|+ ADD +|");
    x += CMD_STEP;
    wmove(wpWin, y, x);
    getyx(wpWin, cmd_y, cmd_x);
    set_cmdL(_del_wp, win_y + cmd_y, win_x + cmd_x);
    wprintw(wpWin, "|- DEL -|");
    x = 1; y += 2;

    for(i = 0; i < NR_WP; ++i){
        if(wp_pool[i].is_used == true){
            if(wp_pool[i].is_changed == true){
                wattron(wpWin, COLOR_PAIR(YELLOW_TEXT));
                mvwprintw(wpWin, y++, x, "%d. %s %x(%d)",
                        wp_pool[i].NO, wp_pool[i].expr, wp_pool[i].val, wp_pool[i].val);
                wattroff(wpWin, COLOR_PAIR(YELLOW_TEXT));
            }
            else mvwprintw(wpWin, y++, x, "%d. %s %x(%d)",
                        wp_pool[i].NO, wp_pool[i].expr, wp_pool[i].val, wp_pool[i].val);
        }
    }

    box(wpWin, BOX_SPE, BOX_SPE);
    wrefresh(wpWin);
}

void draw_All(){
    log("draw_All");
    draw_Menu();
    draw_Reg();
    draw_Text();
    draw_Asm();
    draw_Wp();
    refresh();
}

void add_Text(const char* text, int color){
    log("add_Text");
    log("Text: %s", text);
    int text_Row = TEXT_ROW - BOX_SIDE;
    int text_Col = TEXT_COL - BOX_SIDE;
    int i = 0;
    int len = strlen(text);
    do{
        strncpy(textLine[textPoint], text + (i++)*text_Col, text_Col);
        textColor[textPoint] = color;
        textPoint = (textPoint + 1) % text_Row;
    }while(i*text_Col < len);
    log("textPoint: %d", textPoint);
    draw_Text();
}

void add_Asm(const char* text){
    log("add_Asm");
    log("Asm: %s", text);
    int asm_Row = ASM_ROW - BOX_SIDE;
    int asm_Col = ASM_COL - BOX_SIDE;
    int i;
    int len = strlen(text);
    int new_Col = len / asm_Col;
    new_Col = (new_Col) ? new_Col : 1;
    for(i = 0; i < new_Col; ++i){
        strncpy(asmLine[asmPoint], text + i*asm_Col, asm_Col);
        asmPoint = (asmPoint + 1) % asm_Row;
    }
    log("asmPoint: %d", asmPoint);
    draw_Asm();
}

void set_cmdL(int cmd_id, int y, int x){
    if(cmd_id < 0 || cmd_id >= __END__) return;

    cmdL_list[cmd_id].y = y;
    cmdL_list[cmd_id].x = x;
    log("在(%d, %d)设定事件 %d", y, x, cmd_id);
}

void get_cmdL(int cmd_id, int *y, int *x){
    if(cmd_id < 0 || cmd_id >= __END__) return;

    *y = cmdL_list[cmd_id].y;
    *x = cmdL_list[cmd_id].x;
    log("获取事件%d的坐标(%d, %d)", cmd_id, *y, *x);
}

int find_cmdL(int y, int x){
    int i;
    int l, r, yy;
    log("事件发生位置(%d, %d)", y, x);
    log("正在比对记录中的事件坐标");
    for(i = 0; i < __END__; ++i){
        get_cmdL(i, &yy, &l);
        r = l + CMD_STEP;
        log("记录事件%d: (%d, %d - %d)", i, yy, l, r);
        if((l <= x && x <= r) && y == yy) return i;
    }
    log("未找到匹配的事件");
    return -1;
}

int GUI_helper(){
    int ch;
    do{
        ch = getch();
        log("ch: %d", ch);
        if(ch == KEY_MOUSE){
            getmouse(&mouse);
            log("侦测到鼠标事件 位置(%d, %d)", mouse.y, mouse.x);
            if(mouse.bstate & BUTTON1_CLICKED){
                log("鼠标左键单击");
                switch(find_cmdL(mouse.y, mouse.x)){
                    case _continue: return GUI_c();
                    case _quit: return GUI_q();
                    case _add_wp: return GUI_w();
                    case _del_wp: return GUI_d();
                    case _single_step: return GUI_si(1);
                    case _rebuild: return GUI_rebuild();
                    case _restart: return GUI_restart();
                    default: return 0;
                }
            }
            else if(mouse.bstate & BUTTON1_DOUBLE_CLICKED){
                log("鼠标左键双击");
                switch(find_cmdL(mouse.y, mouse.x)){
                    case _continue: return GUI_c();
                    case _quit: return GUI_q();
                    case _add_wp: return GUI_w();
                    case _del_wp: return GUI_d();
                    case _single_step: return GUI_si(10);
                    case _rebuild: return GUI_rebuild();
                    case _restart: return GUI_restart();
                    default: return 0;
                }
            }
        }
        if(ch == 'r'){
            log("刷新屏幕");
            del_Screen();
            init_Screen();
            draw_All();
            return 0;
        }
        if(ch == 1){
            // Ctrl + a
            log("唤醒控制台");
            return GUI_console();
        }
    }while(ch != KEY_F(1));
    return 0;
}

int GUI_c(){
    log("GUI_c");
    cpu_exec(-1);
    return 0;
}

int GUI_q(){
    log("GUI_q");
    return -1;
}

int GUI_w(){
    log("GUI_w");
    int y = 3, x = 1;
    mvwprintw(wpWin, y, x, "Enter a expr: _____");
    wmove(wpWin, y, x + 14);
    echo();
    curs_set(1);
    char* readline = malloc(1024);
    wgetnstr(wpWin, readline, 1024);
    mvwprintw(wpWin, y, x, "                   ");
    if(wpAdd(readline) == false){
        draw_Wp();
        add_Text("Wrong expression, please check and re-enter", RED_TEXT);
    }
    curs_set(0);
    noecho();
    draw_Wp();
    return 0;
}

int GUI_d(){
    log("GUI_d");
    int y = 3, x = 1;
    mvwprintw(wpWin, y, x, "Enter a id: _______");
    wmove(wpWin, y, x + 12);
    echo();
    curs_set(1);
    char* readline = malloc(1024);
    wgetnstr(wpWin, readline, 1024);
    curs_set(0);
    noecho();
    mvwprintw(wpWin, y, x, "                   ");
    wpDel(readline);
    draw_Wp();
    return 0;
}

int GUI_si(int n){
    log("GUI_si");
    cpu_exec(n);
    return 0;
}

static char special_symbol_table[] = {
    '!', '@', '#', '$', '%',
    '^', '&', '(', ')', '+',
    '-', '*', '/', '='
};
bool check_special_symbol(int ch){
    int size = sizeof(special_symbol_table);
    int i;
    for(i = 0; i < size; ++i){
        if(ch == special_symbol_table[i]) return true;
    }
    return false;
}

char* uio_handle(){
    char* read_line = malloc(1024);
    WINDOW* consoleWin = subwin(stdscr, CONSOLE_ROW, CONSOLE_COL, (MENU_ROW + TEXT_ROW - 3), 0);
    wclear(consoleWin);
    box(consoleWin, '|', '-');
    mvwprintw(consoleWin, 1, 1, "(temu) ");
    wmove(consoleWin, 1, 8);
    echo();
    curs_set(1);
    wgetnstr(consoleWin, read_line, 1024);
    curs_set(0);
    noecho();
    clear();
    delwin(consoleWin);
    draw_All();
    return read_line;
}

int GUI_console(){
    log("GUI_console");
    return GUI_uio();
}

int GUI_rebuild(){
    log("GUI_rebuild");
    FILE* file_modify = fopen("./mips_sc/src/Makefile.testcase", "r");
    if(file_modify == NULL){
        add_Text("Readfile Error", RED_TEXT);
        add_Text("Rebuild failed", RED_TEXT);
        return 0;
    }
    char* build_target = malloc(255*sizeof(char));
    char* new_target = malloc(255*sizeof(char));
    fgets(build_target, INT32_MAX, file_modify);
    fscanf(file_modify, "USER_PROGRAM := %s", build_target);
    fclose(file_modify);
    file_modify = fopen("./mips_sc/src/Makefile.testcase", "w");
    WINDOW* rebuildWin = subwin(stdscr, 4, CONSOLE_COL, 0, 0);
    wclear(rebuildWin);
    box(rebuildWin, '|', '-');
    mvwprintw(rebuildWin, 1, 1, "now_file: %s", build_target);
    mvwprintw(rebuildWin, 2, 1, "enter new file name: ");
    wmove(rebuildWin, 2, 22);
    echo();
    curs_set(1);
    wgetnstr(rebuildWin, new_target, 255);
    curs_set(0);
    noecho();
    clear();
    delwin(rebuildWin);
    if(strlen(new_target) == 0){
        fprintf(file_modify, "# change the tesctcase name\nUSER_PROGRAM := %s", build_target);
    }
    else fprintf(file_modify, "# change the tesctcase name\nUSER_PROGRAM := %s", new_target);
    fclose(file_modify);
    system("make clean_mips && make mips");
    draw_All();
    return GUI_restart();
}

int GUI_restart(){
    log("GUI_restart");
    return -2;
}

char* GUI_getstr(WINDOW* w){
    nocbreak();
    char ch;
    int pos = 0;
    char* str = malloc(1024);
    do{
        noecho();
        ch = wgetch(w);
        log("ch: %d", ch);
        if(ch >= 'A' && ch <= 'z'){
            log("字母\t%c", ch);
            echo();
            str[pos++] = ch;
        }
        else if(ch >= '0' && ch <= '9'){
            log("数字\t%c", ch);
            echo();
            str[pos++] = ch;
        }
        else if(check_special_symbol(ch) == true){
            log("特殊符号\t%c", ch);
            echo();
            str[pos++] = ch;
        }
        else if(ch == KEY_BACKSPACE){
            log("退格键");
            str[pos--] = '\0';
        }
    }while(ch != '\n');
    cbreak();
    return str;
}

// int main(){
//     init_Screen();
//     test_Screen();
//     close_Screen();
//     return 0;
// }